package jianbo;

import com.google.protobuf.InvalidProtocolBufferException;
import redis.clients.jedis.Jedis;
import utgame.UtMessage.MessagePacket;
import utgame.UtMessage.MessagePacket.MessageType;
import utgame.UtMessage.PlayerInfo;
import utgame.UtMessage.GroupInfo;
import utgame.UtMessage.MC_PlayerLogon;
import utgame.UtMessage.MS_PlayerLogon;
import utgame.UtMessage.MC_PlayerLogoff;
import utgame.UtMessage.MS_PlayerLogoff;
import utgame.UtMessage.MC_RequestFriends;
import utgame.UtMessage.MS_RequestFriends;
import utgame.UtMessage.MC_PlayerChat;
import utgame.UtMessage.MS_PlayerChat;
import utgame.UtMessage.OfflineChat;
import utgame.UtMessage.ChatMsg;
import utgame.UtMessage.MC_CreateGroup;
import utgame.UtMessage.MS_CreateGroup;
import utgame.UtMessage.MC_GroupChat;
import utgame.UtMessage.MS_GroupChat;

import java.io.IOException;
import java.util.*;
//import java.util.logging.Logger;

import org.apache.log4j.*;

/**
 * Created by xueal on 2018/1/15.
 */

// 管理数据库中所有玩家
public class PlayerManager {

    public class ReturnStruct{
        public String name;
        public com.google.protobuf.ByteString   msg;
    }

    static Logger logger = Logger.getLogger (PlayerManager.class.getName ());

    public static final String DB_PLAYER                    =     "Player";
    public static final String DB_PLAYER_KEY_SEP            =     "_";

    public static final String DB_OFFLINECHAT               =     "OfflineChat";
    public static final String DB_OFFLINECHAT_KEY_SEP       =     "_";

    public static final String DB_Group                     =     "Group";
    public static final String DB_Group_KEY_SEP             =     "_";

    public static final String DB_WORLD_CHANNEL             =     "World";

    private Jedis mJedis = null;

    private static final PlayerManager single = new PlayerManager();

    private static String makePlayerKey(String uuid, String name){
        return String.join(DB_PLAYER_KEY_SEP, new String[]{uuid, name});
    }
    private static String makeGroupKey(String uuid, String name){
        return String.join(DB_Group_KEY_SEP, new String[]{uuid, name});
    }
    private static String makeOfflineChatKey(String receiver, String sender){
        return String.join(DB_OFFLINECHAT_KEY_SEP, new String[]{receiver, sender});
    }
    private PlayerManager() {
        Init();
    }

    //静态工厂方法
    public static PlayerManager getInstance() {
        return single;
    }

    public void Init(){
        if (mJedis != null) return;
        mJedis = RedisUtil.getJedis();

        // 初始化redis数据库
        String worldChannelUuid = UUID.randomUUID().toString();

        boolean isWorldChannelExist = false;

        Map<byte[], byte[]> groupInfoMap = mJedis.hgetAll(DB_Group.getBytes());
        for (Map.Entry<byte[], byte[]> entry : groupInfoMap.entrySet()) {
            String key = new String(entry.getKey());
            String groupName = key.substring(key.indexOf(DB_Group_KEY_SEP) + 1);
            if (DB_WORLD_CHANNEL.equals(groupName)){
                isWorldChannelExist = true;
                break;
            }
        }

        if (!isWorldChannelExist){
            GroupInfo.Builder groupInfoBuilder = GroupInfo.newBuilder();
            groupInfoBuilder.setId(worldChannelUuid);
            groupInfoBuilder.setName(DB_WORLD_CHANNEL);

            mJedis.hset(DB_Group.getBytes(), makeGroupKey(worldChannelUuid, DB_WORLD_CHANNEL).getBytes(),
                    groupInfoBuilder.build().toByteArray());
        }
    }

    public String addPlayer(String name, String pwd) {

        String playerUuid = UUID.randomUUID().toString();

        PlayerInfo.Builder builder = PlayerInfo.newBuilder();
        builder.setName(name);
        builder.setPwd(pwd);
        builder.setId(playerUuid);
        builder.setCreateTime(new Date(System.currentTimeMillis()).getTime());

        mJedis.hset(DB_PLAYER.getBytes(), makePlayerKey(playerUuid, name).getBytes(), builder.build().toByteArray());

        return playerUuid;
    }

    public PlayerInfo getPlayer(String id){

        PlayerInfo playerInfo = null;
        try {
            Map<byte[], byte[]> playerInfoMap = mJedis.hgetAll(DB_PLAYER.getBytes());
            for (Map.Entry<byte[], byte[]> entry : playerInfoMap.entrySet()) {
                String key = new String(entry.getKey());
                String playerId = key.substring(0, key.indexOf(DB_PLAYER_KEY_SEP));
                if (id.equals(playerId)){
                    playerInfo = PlayerInfo.parseFrom(entry.getValue());
                }
            }
        } catch (InvalidProtocolBufferException e) {
            e.printStackTrace();
        }
        return playerInfo;
    }

    public List<PlayerInfo> getAllPlayer(){

        List<PlayerInfo> playerInfoList = new ArrayList<>();

        Map<byte[], byte[]> playerInfoMap = mJedis.hgetAll(DB_PLAYER.getBytes());
        for (Map.Entry<byte[], byte[]> entry : playerInfoMap.entrySet()) {
            PlayerInfo playerInfo = null;
            try {
                String key = new String(entry.getKey());
                String playerId = key.substring(0, key.indexOf(DB_PLAYER_KEY_SEP));
                playerInfo = PlayerInfo.parseFrom(entry.getValue());
            } catch (InvalidProtocolBufferException e) {
                e.printStackTrace();
            }
            if (playerInfo!= null){
                playerInfoList.add(playerInfo);
            }
        }
        return playerInfoList;
    }

    public void updateOnline(String name, boolean isOnline){
        if (name == null) return;

        Map<byte[], byte[]> playerInfoMap = mJedis.hgetAll(DB_PLAYER.getBytes());

        for (Map.Entry<byte[], byte[]> entry : playerInfoMap.entrySet()) {
            String key = new String(entry.getKey());
            String playerName = key.substring(key.indexOf(DB_PLAYER_KEY_SEP) + 1);
            if (name.equals(playerName)){
                try {
                    PlayerInfo.Builder builder = PlayerInfo.parseFrom(entry.getValue()).toBuilder();
                    builder.setIsOnline(isOnline);

                    mJedis.hset(DB_PLAYER.getBytes(), makePlayerKey(builder.getId(), builder.getName()).getBytes(), builder.build().toByteArray());

                } catch (InvalidProtocolBufferException e) {
                    e.printStackTrace();
                }
            }
        }

    }

    public boolean isPlayerExist(String name){
        if (name == null) return false;

        Map<byte[], byte[]> playerInfoMap = mJedis.hgetAll(DB_PLAYER.getBytes());

        for (Map.Entry<byte[], byte[]> entry : playerInfoMap.entrySet()) {
            String key = new String(entry.getKey());
            String playerName = key.substring(key.indexOf(DB_PLAYER_KEY_SEP) + 1);
            if (name.equals(playerName)){
                return true;
            }
        }
        return false;
    }

    public boolean isPwdValid(String name, String pwd){
        if (name == null || pwd == null) return false;

        Map<byte[], byte[]> playerInfoMap = mJedis.hgetAll(DB_PLAYER.getBytes());

        for (Map.Entry<byte[], byte[]> entry : playerInfoMap.entrySet()) {
            String key = new String(entry.getKey());
            String playerName = key.substring(key.indexOf(DB_PLAYER_KEY_SEP) + 1);
            if (name.equals(playerName)){
                try {
                    PlayerInfo playerInfo = PlayerInfo.parseFrom(entry.getValue());
                    if (playerInfo.getPwd().equals(pwd)){
                        return true;
                    }
                } catch (InvalidProtocolBufferException e) {
                    e.printStackTrace();
                }
            }
        }
        return false;
    }

    public com.google.protobuf.ByteString handleMessage(String name, byte[] message){
        try {
            MessagePacket messagePacket = MessagePacket.parseFrom(message);

            MessagePacket.MessageType msgType = messagePacket.getType();
            logger.debug("msg_type:" + msgType);
            if (msgType == MessageType.PlayerLogon)
            {
                MC_PlayerLogon playerLogon = MC_PlayerLogon.parseFrom(messagePacket.getMsg());
                return handlePlayerLogon(name, playerLogon);
            }
            else if (msgType == MessageType.PlayerLogoff){
                MC_PlayerLogoff playerLogoff = MC_PlayerLogoff.parseFrom(messagePacket.getMsg());
                return handlePlayerLogoff(name, playerLogoff);
            }
            else if (msgType == MessageType.RequestFriends){
                MC_RequestFriends requestFriends = MC_RequestFriends.parseFrom(messagePacket.getMsg());
                return handleRequestFriends(name, requestFriends);
            }
            else if (msgType == MessageType.PlayerChat){
                MC_PlayerChat chat = MC_PlayerChat.parseFrom(messagePacket.getMsg());
                return handlePlayerChat(name, chat);
            }
            else if (msgType == MessageType.GroupChat){
                MC_GroupChat chat = MC_GroupChat.parseFrom(messagePacket.getMsg());
                return handleGroupChat(name, chat);
            }
            else if (msgType == MessageType.CreateGroup){
                MC_CreateGroup mcCreateGroup = MC_CreateGroup.parseFrom(messagePacket.getMsg());
                return handleCreateGroup(name, mcCreateGroup);
            }
            else if (MessageType.PlayerActionBegin_VALUE < msgType.getNumber() &&
                    msgType.getNumber() < MessageType.PlayerActionEnd_VALUE){
                ClientSession.getInstance().broadcastPlayerAction(msgType, messagePacket.getMsg());
            }
        } catch (InvalidProtocolBufferException e) {
            e.printStackTrace();
        } catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

    public com.google.protobuf.ByteString handlePlayerLogon(String name, MC_PlayerLogon message){

        int retCode = 0;
        if (message.getIsRegister()){
            if (isPlayerExist(message.getName())){
                retCode = 1;
            }
            else {
                addPlayer(message.getName(), message.getPwd());
            }
        }
        else {
            if (!isPlayerExist(message.getName())) {
                retCode = 2;
            }
            else if (!isPwdValid(message.getName(), message.getPwd())){
                retCode = 3;
            }
        }

        MS_PlayerLogon.Builder builder = MS_PlayerLogon.newBuilder();
        builder.setRet(retCode);
        builder.setName(message.getName());
        builder.setPwd(message.getPwd());

        return builder.build().toByteString();
//        try {
//            // 此处name == null
//            jianbo.ClientSession.getInstance().getClient(message.getName()).sendMessage(MessageType.PlayerLogon, builder.build().toByteString());
//        } catch (IOException e) {
//            e.printStackTrace();
//        }
//        return null;
    }

    public com.google.protobuf.ByteString handlePlayerLogoff(String name, MC_PlayerLogoff message){

        ClientSession.getInstance().removeClient(message.getName());
        ClientSession.getInstance().broadcastPlayerOffline(message.getName());

        MS_PlayerLogoff.Builder builder = MS_PlayerLogoff.newBuilder();
        builder.setName(message.getName());

        return builder.build().toByteString();
    }

    public com.google.protobuf.ByteString handleRequestFriends(String name, MC_RequestFriends message){

        MS_RequestFriends.Builder builder = MS_RequestFriends.newBuilder();

        Map<byte[], byte[]> playerInfoMap = mJedis.hgetAll(DB_PLAYER.getBytes());
        for (Map.Entry<byte[], byte[]> entry : playerInfoMap.entrySet()) {
            try {
                PlayerInfo playerInfo = PlayerInfo.parseFrom(entry.getValue());
                builder.addFriend(playerInfo);

            } catch (InvalidProtocolBufferException e) {
                e.printStackTrace();
            }
        }

        Map<byte[], byte[]> groupInfoMap = mJedis.hgetAll(DB_Group.getBytes());
        for (Map.Entry<byte[], byte[]> entry : groupInfoMap.entrySet()) {
            try {
                GroupInfo groupInfo = GroupInfo.parseFrom(entry.getValue());

                if (groupInfo.getName().equals(DB_WORLD_CHANNEL)){
                    builder.addGroup(groupInfo);
                    continue;
                }

                List<String> memberList = groupInfo.getMemberNameList();
                for (String memberName : memberList){
                    if (name.equals(memberName)){
                        builder.addGroup(groupInfo);
                        break;
                    }
                }

            } catch (InvalidProtocolBufferException e) {
                e.printStackTrace();
            }
        }


        try {
            ClientSession.getInstance().getClient(name).sendMessage(MessageType.RequestFriends, builder.build().toByteString());
            return builder.build().toByteString();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    public com.google.protobuf.ByteString handlePlayerChat(String name, MC_PlayerChat message){

        MS_PlayerChat.Builder builder = MS_PlayerChat.newBuilder();
        builder.setSenderName(name);
        builder.setChatContent(message.getChatContent());

        com.google.protobuf.ByteString msgRet = builder.build().toByteString();


        try {
            WebSocketController client =  ClientSession.getInstance().getClient(message.getReceiverName());

            if (client == null) {   // 处理离线消息
                saveOfflineChat(message.getReceiverName(), name, message.getChatContent());
            } else {
                client.sendMessage(MessageType.PlayerChat, msgRet);
            }
            return msgRet;
        } catch (IOException e) {
            e.printStackTrace();
        }

        return msgRet;
    }

    public com.google.protobuf.ByteString handleGroupChat(String name, MC_GroupChat message) {

        MS_GroupChat.Builder builder = MS_GroupChat.newBuilder();
        builder.setGroupName(message.getGroupName());
        builder.setSenderName(name);
        builder.setChatContent(message.getChatContent());

        com.google.protobuf.ByteString msgRet = builder.build().toByteString();


        Map<byte[], byte[]> groupInfoMap = mJedis.hgetAll(DB_Group.getBytes());
        for (Map.Entry<byte[], byte[]> entry : groupInfoMap.entrySet()) {
            try {
                GroupInfo groupInfo = GroupInfo.parseFrom(entry.getValue());
                if (groupInfo.getName().equals(message.getGroupName())){

                    if (message.getGroupName().equals(DB_WORLD_CHANNEL)){
                        ClientSession.getInstance().broadcastPlayerAction(MessageType.GroupChat, msgRet);
                    } else {
                        List<String> memberList = groupInfo.getMemberNameList();
                        for (String memberName : memberList){
                            WebSocketController client =  ClientSession.getInstance().getClient(memberName);
                            if (client != null){        // 群聊不处理离线情况
                                client.sendMessage(MessageType.GroupChat, msgRet);
                            }
                        }
                    }
                    break;
                }
            } catch (InvalidProtocolBufferException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return msgRet;
    }


    public com.google.protobuf.ByteString handleCreateGroup(String name, MC_CreateGroup message){

        boolean isGroupExist = false;
        Map<byte[], byte[]> groupInfoMap = mJedis.hgetAll(DB_Group.getBytes());
        for (Map.Entry<byte[], byte[]> entry : groupInfoMap.entrySet()) {
            try {
                GroupInfo groupInfo = GroupInfo.parseFrom(entry.getValue());
                if (groupInfo.getName().equals(message.getGroupName())){
                    isGroupExist = true;
                    break;
                }
            } catch (InvalidProtocolBufferException e) {
                e.printStackTrace();
            }
        }

        MS_CreateGroup.Builder createGroupBuilder = MS_CreateGroup.newBuilder();

        if (isGroupExist){
            createGroupBuilder.setErrorCode(1);
        } else {

            createGroupBuilder.setErrorCode(0);

            GroupInfo.Builder groupInfoBuilder = GroupInfo.newBuilder();

            String groupUuid = UUID.randomUUID().toString();
            groupInfoBuilder.setId(groupUuid);
            groupInfoBuilder.setName(message.getGroupName());
            groupInfoBuilder.setCreateTime(System.currentTimeMillis());
            groupInfoBuilder.setCreatorName(message.getCreatorName());

            List<String> memberNameList = message.getMemberNameList();
            for (String memberName : memberNameList){
                groupInfoBuilder.addMemberName(memberName);
            }

            createGroupBuilder.setGroup(groupInfoBuilder);

            mJedis.hset(DB_Group.getBytes(), makeGroupKey(groupUuid, message.getGroupName()).getBytes(),
                    groupInfoBuilder.build().toByteArray());
        }

        try {
            ClientSession.getInstance().getClient(name).sendMessage(MessageType.CreateGroup, createGroupBuilder.build().toByteString());
            return createGroupBuilder.build().toByteString();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    private void saveOfflineChat(String receiver, String sender, String msg){

        byte[] offlineChat = mJedis.hget(DB_OFFLINECHAT.getBytes(), makeOfflineChatKey(receiver, sender).getBytes());

        OfflineChat.Builder builderOfflineChat = null;

        if (offlineChat != null && offlineChat.length != 0){
            try {
                OfflineChat chatPrev = OfflineChat.parseFrom(offlineChat);
                builderOfflineChat = chatPrev.toBuilder();
            } catch (InvalidProtocolBufferException e) {
                e.printStackTrace();
            }
        }
        if (builderOfflineChat == null){
            builderOfflineChat = OfflineChat.newBuilder();
        }

        ChatMsg.Builder builderChatMsg = ChatMsg.newBuilder();
        builderChatMsg.setReveiver(receiver);
        builderChatMsg.setSender(sender);
        builderChatMsg.setContent(msg);
        builderChatMsg.setTime(System.currentTimeMillis());

        builderOfflineChat.addMsg(builderChatMsg);

        mJedis.hset(DB_OFFLINECHAT.getBytes(), makeOfflineChatKey(receiver, sender).getBytes(), builderOfflineChat.build().toByteArray());
    }

//    private void removeOfflineChat(String receiver, String sender){
//        mJedis.hdel(DB_OFFLINECHAT_KEY_SEP.getBytes(), makeOfflineChatKey(receiver, sender).getBytes());
//    }
    public void removeOfflineChat(String receiver){

        Map<byte[], byte[]> offlineChatMap = mJedis.hgetAll(DB_OFFLINECHAT.getBytes());
        for (Map.Entry<byte[], byte[]> entry : offlineChatMap.entrySet()) {
            String key = new String(entry.getKey());
            String receiverName = key.substring(0, key.indexOf(DB_OFFLINECHAT_KEY_SEP));
            String senderName = key.substring(key.indexOf(DB_OFFLINECHAT_KEY_SEP) + 1);
            if (receiver.equals(receiverName)){
                mJedis.hdel(DB_OFFLINECHAT.getBytes(), entry.getKey());
            }
        }
    }

    public ArrayList<byte[]> getOfflineChat(String receiver){

        if (receiver == null) return null;

        ArrayList<byte[]> offlineChatList = new ArrayList<>();

        Map<byte[], byte[]> offlineChatMap = mJedis.hgetAll(DB_OFFLINECHAT.getBytes());
        for (Map.Entry<byte[], byte[]> entry : offlineChatMap.entrySet()) {
            String key = new String(entry.getKey());
            String receiverName = key.substring(0, key.indexOf(DB_OFFLINECHAT_KEY_SEP));
            if (receiver.equals(receiverName)){
                offlineChatList.add(entry.getValue());
            }
        }
        return offlineChatList;
    }

}


